#include<iostream>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<dirent.h>
#include<string.h>
#include<ftw.h>
#include<stdlib.h>
#include <errno.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include <fcntl.h>
#include<queue>
using namespace std;
char* Pwd();
char* List(char (*orderList)[1024],int xindex);
int Modifydir(char (*orderList)[1024],int xlength,char *errorStatus);
int Makedir(char (*orderList)[1024],char **errorList,int xlength);
int Deldir(char (*orderList)[1024],char**errorList,int xlength);
int Rename(char (*orderList)[1024],char*errorList,int xlength);
int Copy(char (*orderList)[1024],char *errorList,int xlength);
int Find(char (*orderList)[1024],char *errorList,int xlength,char *absolutePath);
int FindFile(char *path,char *fileName,char *url);
int FindFile2(char *path,char *fileName,char *url);
char* ErrorStatus();
int isFileExist(const char *path);
int isDirectory(const char *path);
int copyFile(const char *sourceFile,const char *desFile);
void printfError();
char *currentUrl = NULL;
int MAX_LENGTH = 1024;
int main()
{
    while(true)
    {
        char order[1024] = {0};
        currentUrl = Pwd();
        printf("%s$",currentUrl);
        cin.getline(order,1024);
        char orderList[100][1024]={{0}};
        int xindex = 0;
        int yindex = 0;
        if(strcmp(order,"exit") == 0)
        {
            return 0;
        }
        for(int i=0;i<strlen(order);i++)
        {
            if(order[i] != ' ')
            {
                orderList[xindex][yindex++] = order[i]; 
            }
            else
            {
                if(i<1024-1 && order[i+1] != ' ' && order[i+1] != 0)
                {
                    xindex++;
                }
                yindex = 0;
            }
        }
        if(strcmp(order,"pwd") == 0)
        {
            char *url = Pwd();
            printf("%s\n",url);
            free(url);
            url = NULL;
        }
        else if(strcmp(orderList[0],"list") == 0)
        {
            char *catalog = List(orderList,xindex);
            if(catalog != NULL)
           {
                printf("%s\n",catalog);
                free(catalog);
                catalog = NULL;
            }
        }
        else if(strcmp(orderList[0],"modifydir") == 0)
        {
            char errorStatus[1024] = {0};
            int status = Modifydir(orderList,xindex,errorStatus);
            if(status == 0)
            {
                printf("%s\n",errorStatus);
            }
        }
        else if(strcmp(orderList[0],"makedir") == 0)
        {
            char **errorList = (char**)malloc(sizeof(char*)*1024);
            int errorListLength =  Makedir(orderList,errorList,xindex);
           int index = 0;
           if(errorListLength == -1)
           {
               printf("%s\n",errorList[0]);
               free(errorList[0]);
               errorList[0] = NULL;
           }
           for(int i=0;i<errorListLength;i++)
           {
            //    printf("mkdir: 无法创建:\"");
            //    int k=0;
            //    for(k=0;errorList[i][k] != ' ';k++)
            //    {
            //        printf("%c",errorList[i][k]);
            //    }
            //    printf("\":");
            //    for(;k<strlen(errorList[i]);k++)
            //    {
            //        printf("%c",errorList[i][k]);
            //    }
            //    printf("\n");
            printf("%s\n",errorList[i]);
               free(errorList[i]);
               errorList[i] = NULL;
           }
           free(errorList);
           errorList = NULL;
        }
        else if(strcmp(orderList[0],"deldir") == 0)
        {
            char **errorList = (char**)malloc(sizeof(char*)*1024);
            int length = Deldir(orderList,errorList,xindex);
            if(length == -1)
            {
                printf("%s\n",errorList[0]);
                free(errorList[0]);
               errorList[0] = NULL;
            }
            else
            {
                for(int i=0;i<length;i++)
                {
                    printf("dedir: 删除:\"");
                    int k=0;
                    for(k=0;errorList[i][k] != ' ';k++)
                    {
                        printf("%c",errorList[i][k]);
                    }
                    printf("\"失败 :");
                    for(;k<strlen(errorList[i]);k++)
                    {
                        printf("%c",errorList[i][k]);
                    }
                    printf("\n");
                    free(errorList[i]);
                    errorList[i] = NULL;
                }
            }
            
        }
        else if(strcmp(orderList[0],"rename") == 0)
        {
            char *errorList = (char*)malloc(sizeof(char)*MAX_LENGTH);
            memset(errorList,0,MAX_LENGTH);
            int status = Rename(orderList,errorList,xindex);
            if(status == -1)
            {
                printf("%s\n",errorList);
                free(errorList);
                errorList = NULL;
            }
        }
        else if(strcmp(orderList[0],"copy") == 0)
        {
            char *errorList = (char*)malloc(sizeof(char)*MAX_LENGTH);
            memset(errorList,0,MAX_LENGTH);
            int judge = Copy(orderList,errorList,xindex);
            if(judge == -1)
            {
                printf("%s\n",errorList);
            }
            free(errorList);
            errorList = NULL;
        }
        else if(strcmp(orderList[0],"find") == 0)
        {
            char errorList[1024] = {0};
            char absolutePath[1024] = {0};
            int status = Find(orderList,errorList,xindex,absolutePath);
            if(status == -1)
            {
                printf("%s\n",errorList);
            }
            else
            {
                printf("%s\n",absolutePath);
            }
            
        }
        else{
            printf("%s command error\n",orderList[0]);
        }
    }
    wait(NULL);
    return 0;

}

char* Pwd()
{
    DIR *dirp = NULL;
    struct dirent *currentDirent = NULL, *chaildDirent = NULL;
    char path[100][1024] = {{0}}; 
    char *url = (char*)malloc(sizeof(char)*1024);
    memset(url,0,1024);
    int index = 0;
    while(true)
    {
        dirp = opendir(".");
        do
        {
            currentDirent = readdir(dirp);
        } while (strcmp(currentDirent->d_name,".") != 0);
        ino_t pointNo = currentDirent->d_ino;
        closedir(dirp);
        dirp = opendir("..");
        do
        {
            currentDirent = readdir(dirp);
        } while (strcmp(currentDirent->d_name,"..") != 0);
        closedir(dirp);
        if(pointNo == currentDirent->d_ino)
        {
            break;
        }
        chdir("..");
        dirp = opendir(".");
        do
        {
            /* code */
            currentDirent = readdir(dirp);
        } while (currentDirent->d_ino != pointNo);
        strcpy(path[index++],currentDirent->d_name);
        closedir(dirp);
    }
   
    for(int i=index -1;i>0;i--)
    {
        chdir(path[i]);
        strcat(url,path[i]);
        url[strlen(url)] = '/';
    }
    chdir(path[0]);
    strcat(url,path[0]);
    return url;
}

char* List(char (*orderList)[1024],int xlength)
{
    int status = -1;
    char* catalog = (char*)malloc(sizeof(char)*MAX_LENGTH);
    char *path = NULL;
    int judgeA = 0;
    int judgea = 0;
    int judgeP = 0;
    DIR *dirp = NULL;
    struct dirent *dp;
    int index = 0;
    if((strcmp(orderList[1],"-a") != 0 && strcmp(orderList[1],"-A") != 0 && xlength > 1))
    {
        printf("command error %s\n",orderList[1]);
        return NULL;
    }
    if(xlength > 2)
    {
        printf("command error %s\n",orderList[3]);
        return NULL;
    }
    if(xlength == 2)
    {
        path = orderList[2];
        judgeP = 1;
    }
    if(strcmp(orderList[1],"-a") == 0)
    {
        judgea = 1;
    }
    else if(strcmp(orderList[1],"-A") == 0)
    {
        judgeA = 1;
    }
    else if(xlength == 1)
    {
        judgeP = 1;
        path = orderList[1];
    }
    memset(catalog,0,MAX_LENGTH);
    if(judgeP)
    {
        // printf("path = %s\n",path);
        status = chdir(path);
        if(status == -1)
        {    
            return ErrorStatus();;
        }
    }
    dirp = opendir(".");
    while((dp=readdir(dirp)) != NULL)
    {
        if(judgea)
        {
            strcat(catalog,dp->d_name);
            catalog[strlen(catalog)] = ' ';
        }
        else if(judgeA)
        {
            if(strcmp(dp->d_name,".") != 0 && strcmp(dp->d_name,"..") !=0)
            {
                strcat(catalog,dp->d_name);
                catalog[strlen(catalog)] = ' ';
            }
        }
        else
        {
            if(dp->d_name[0] != '.')
            {
                strcat(catalog,dp->d_name);
                catalog[strlen(catalog)] = ' ';
            }
        }
    }  
    closedir(dirp);
     catalog[strlen(catalog)] = '\n';
     char url[100] = {0};
     url[0] = '/';
     strcat(url,currentUrl);
    status = chdir(url);
    if(status == -1)
    {
        char *errorStatus = ErrorStatus();
        return errorStatus;
    }
    
    return catalog;
    
}

int Modifydir(char (*orderList)[1024],int xlength,char *errorStatus)
{
    if(xlength >1)
    {
        strcat(errorStatus,"操作数错误");
        return 0;
    }
    int status = chdir(orderList[1]);
    if(status == -1)
    {
        // ChdirErrnoStatus();
        char *error = ErrorStatus();
        strcat(errorStatus,error);
        free(error);
        error = NULL;
        return 0;
    }
    return 1;
}

int Makedir(char (*orderList)[1024],char **errorList,int xlength)
{
    // char **errorList = (char**)malloc(sizeof(char*)*1024);
    int status = -1;
    errno = 0;
    int index = 0;
    if(xlength == 0)
    {
        errorList[index] = (char*)malloc(sizeof(char)*1024);
        strcat(errorList[index],orderList[0]);
        strcat(errorList[index]," : 缺少操作数");
        return -1;
    }
    for(int i=1;i<=xlength;i++)
    {
        status = mkdir(orderList[i],S_IREAD|S_IRWXG|S_IROTH);
        if(status == -1)
        {
            errorList[index] = (char*)malloc(sizeof(char)*1024);
            // char *errorchar = errorList[index];
            memset(errorList[index],0,1024);
            // printf("errorList open = %s\n",errorList[index]);
            strcat(errorList[index],"mkdir: 无法创建:\"");
            strcat(errorList[index],orderList[i]);
            errorList[index][strlen(errorList[index])] = '"';
            errorList[index][strlen(errorList[index])] = ':';
            char *errorStatus = ErrorStatus();
            strcat(errorList[index],errorStatus); //把错误连到后面
            // errorList[index][strlen(errorList[index])] = '\n';
            free(errorStatus);
             errorStatus = NULL;
            index++;
        }
    }
   return index;
}

int Deldir(char (*orderList)[1024],char**errorList,int xlength)
{
    errno = 0;
    int index = 0;
    int status = -1;
    if(xlength == 0)
    {
        errorList[index] = (char*)malloc(sizeof(char)*1024);
        strcat(errorList[index],orderList[0]);
        strcat(errorList[index]," : 缺少操作数");
        return -1;
    }
    for(int i=1;i<=xlength;i++)
    {
        status = rmdir(orderList[i]);
        if(status == -1)
        {
            errorList[index] = (char*)malloc(sizeof(char)*1024);
            memset(errorList[index],0,1024);
            strcat(errorList[index],orderList[i]);
            errorList[index][strlen(errorList[index])] = ' '; //空格分割开
            char *errorStatus = ErrorStatus();
            strcat(errorList[index],errorStatus); //把错误连到后面
            free(errorStatus);
            errorStatus = NULL;
            index++;
        }
    }
    return index;
}

int Rename(char (*orderList)[1024],char*errorList,int xlength)
{
    int status = -1;
    if(xlength != 2)
    {
        strcat(errorList,"操作数错误");
        return -1;
    }
    status = rename(orderList[1],orderList[2]);
    if(status == -1)
    {
        char *errorStatus = ErrorStatus(); 
        strcat(errorList,errorStatus);
        free(errorStatus);
        errorStatus = NULL;
        return -1;
    }
    return 0;
}

int Copy(char(*orderList)[1024],char *errorList,int xlength)
{
    int status = 0;
    if(xlength < 2)
    {
        strcat(errorList,orderList[0]);
        strcat(errorList," : 缺少文件操作数");
        return -1;
    }
    if(xlength > 3)
    {
        strcat(errorList,orderList[0]);
        strcat(errorList, " : 操作数过多");
        return -1;
    }
    if(xlength == 3)
    {
        if(strcmp(orderList[1],"-i") != 0 )
        {
            strcat(errorList,orderList[0]);
            strcat(errorList," : 参数错误");
            return -1;
        }
        if(isDirectory(orderList[2]))
        {
            strcat(errorList,orderList[2]);
            strcat(errorList," : 文件类形错误");
            return -1;
        }
        if(isDirectory(orderList[3]))
        {
           strcat(errorList,orderList[3]);
           strcat(errorList," : 文件类形错误");
           return -1;
        }
        if (isFileExist(orderList[3]))
        {
            printf("%s : 是否覆盖 %s : ",orderList[0],orderList[3]);
            char judge = 0;
                //getchar();//接收回车
            judge = getchar();
            getchar();
            if(judge == 'y' || judge == 'Y')
            {
                status = copyFile(orderList[2],orderList[3]);
            }
        }
        else
        {
            status = copyFile(orderList[2],orderList[3]);
        }  
    }
    else
    {
        if(isDirectory(orderList[1]))
        {
            strcat(errorList,orderList[1]);
             strcat(errorList," : 文件类形错误");
             return -1;
        }
        if(isDirectory(orderList[2]))
        {
            strcat(errorList,orderList[2]);
            strcat(errorList," : 文件类形错误");
            return -1;
        }
        status = copyFile(orderList[1],orderList[2]);
    }
    if(status == 0)
    {
        strcat(errorList,orderList[0]);
        strcat(errorList," : 复制出错");
        return -1;
    }
    return 1;
    
}

int copyFile(const char *sourceFile,const char *desFile)
{
    FILE *fpr = fopen(sourceFile,"r");
    FILE *fpw = fopen(desFile,"w");
    if(fpr == NULL || fpw == NULL)
    {
        return 0;
    }
    char ch = fgetc(fpr);
    while(!feof(fpr))
    {
        fputc(ch,fpw);
        ch = fgetc(fpr);
    }
    fclose(fpr);
    fclose(fpw);
    return 1;
}

int Find(char (*orderList)[1024],char *errorList,int xlength,char *absolutePath)
{
    char backPath[1024] = {0};
    if(xlength != 2)
    {
        strcat(errorList,orderList[0]);
        strcat(errorList," : 操作数错误");
        return -1;
    }
    int status = FindFile2(orderList[1],orderList[2],absolutePath);
    backPath[0] = '/';
    strcat(backPath,currentUrl);
    chdir(backPath);
    if(status)
    {
       return 1;
    }
    strcat(errorList,"未能找到文件");
    return -1;
    // FindFile(orderList[1],orderList[2],url);
    // char* pwd = Pwd();
    // strcat(absolutePath,pwd);
    // free(pwd);
    // pwd = NULL;
    // int absolutePathLength = strlen(absolutePath);
    // int index = 0;
    // char **direntList = (char**)malloc(sizeof(char*)*MAX_LENGTH);
    // direntList[0] = (char*)malloc(sizeof(char*)*MAX_LENGTH);
    // for(int i=1;i<strlen(url);i++)
    // {
    //     if(url[i] == '.' || url[i] == '/')
    //     {
    //         continue;
    //     }
    //     if(url[i] != ' ')
    //     {
    //         direntList[index][strlen(direntList[index])] = url[i];
    //     }
    //     else
    //     {
    //         direntList[++index] = (char*)malloc(sizeof(char*)*MAX_LENGTH);
    //     }
    // }
    // for(int i=index;i>=0;i--)
    // {
    //     absolutePath[strlen(absolutePath)] = '/';
    //     strcat(absolutePath,direntList[i]);
    //     free(direntList[i]);
    //     direntList[i] = NULL;
    // }
    // free(direntList);
    // direntList = NULL;
    // char backUrl[100] = {0};
    // backUrl[0] = '/';
    // strcat(backUrl,currentUrl);
    // int status = chdir(backUrl); //返回到当前路径
    // if(status == -1)
    // {
    //     char error[100];
    //     perror(error);
    //     printf("%s\n",error); 
    // }
    return 0;
}

int FindFile(char *path,char *fileName,char *url)
{
    DIR *dir = NULL;
    struct dirent *dp = NULL;
    int status = -1;
    status = chdir(path);
    if(status == -1)
    {
        return 0;
    }
    dir = opendir("."); 
    if(dir == NULL)
    {
        chdir("..");
        return 0;
    }
    dp = readdir(dir);
    while (dp != NULL)
    {
        if(strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0)
        {
            dp = readdir(dir);
            continue;
        }
        if(strcmp(dp->d_name,fileName) == 0 || FindFile(dp->d_name,fileName,url) == 1)
        {
            url[strlen(url)] = ' ';
            strcat(url,path);
            closedir(dir);
            chdir("..");
            return 1;
        }
        dp = readdir(dir);
    }
    closedir(dir);
    chdir("..");
    return 0;  
}

int FindFile2(char *path,char *fileName,char *url)
{
    queue<string> nameQueue;
    nameQueue.push(path);
    DIR *dir;
    struct dirent *dp ;
    string currentPath;
    while(!nameQueue.empty())
    {
        currentPath = nameQueue.front();
        nameQueue.pop();
        if(chdir(currentPath.c_str()) == -1)
        {
            continue;
        }
        dir = opendir(".");
        if(dir == NULL)
        {
            continue;
        }
        dp = readdir(dir);
        while(dp != NULL)
        {
            if(strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0 )
            {
                dp = readdir(dir);
                continue;
            }
            if(strcmp(dp->d_name,fileName) == 0)
            {
                char *pwd = Pwd();
                strcat(url,pwd);
                free(pwd);
                pwd = NULL;
                closedir(dir);
                return 1;

            }
            if(isDirectory(dp->d_name))
            {
                char *pwd = Pwd();
                char nextPath[1024] = {0};
                nextPath[0] = '/';
                strcat(nextPath,pwd);
                nextPath[strlen(nextPath)] = '/';
                strcat(nextPath,dp->d_name);
                free(pwd);
                pwd = NULL;
                nameQueue.push(nextPath);
            }
            dp = readdir(dir);
        }
        closedir(dir);
    }
    return 0;

}

void printfError()
{
    char *error = ErrorStatus();
    printf("error : %s\n",error);
    free(error);
    error = NULL;
}

char* ErrorStatus()
{
    char *errorStatus = (char*)malloc(sizeof(char)*MAX_LENGTH);
    memset(errorStatus,0,1024);
    switch (errno)
    {
    case EACCES:
        strcat(errorStatus,"在路径前缀的某个组件上拒绝搜索权限，或在要创建的目录的父目录上拒绝写权限。");
        /* code */
        break;
    case EEXIST:
        strcat(errorStatus,"文件名已存在");
        break;
    case EMLINK:
        strcat(errorStatus,"在解析路径参数时遇到的符号链接中存在循环");
        break;
    case ENAMETOOLONG:
        strcat(errorStatus,"路径过长超出{PATH_MAX}或着文件名过长超过{NAME_MAX}");
        break;
    case ENOENT:
        strcat(errorStatus,"路径前缀的组件指定的路径热名称的现有目录或路径是一个空字符串");
        break;
    case ENOSPC:
        strcat(errorStatus,"文件系统没有足够的空间容纳新目录的内容或扩展新目录的父目录");
        break;
    case ENOTDIR:
        strcat(errorStatus,"路径前缀的组成部分不是目录");
        break;        
    case EBUSY:
        strcat(errorStatus,"目录正在被使用或错误实现");
        break;
    case ENOTEMPTY:
        strcat(errorStatus,"目录不为空，操作失败");
        break;
    case EINVAL:
        strcat(errorStatus,"新的路径名包含旧的路径前缀，或者尝试将目录本身的子目录");
        break;
    case EIO:
        strcat(errorStatus,"发生物理I/O异常");
        break;
    case ELOOP:
        strcat(errorStatus,"解析路径参数期间遇到了超过{SYMLOOP_MAX}个符号链接");
        break;
    case EPERM:
        strcat(errorStatus,"父目录有S_ISVTX标记，调用访不是目录所有者");
        break;
    case EROFS:
        strcat(errorStatus,"要删除的目录条目位于只读文件系统上");
        break;
    case EDQUOT:
        strcat(errorStatus,"用户对文件系统上磁盘块的配额为枯竭");
        break;
    case EFAULT:
        strcat(errorStatus,"新路径或旧路径存在点");
        break;
    case EISDIR :
        strcat(errorStatus,"存在不是目录");
        break;
    }
    return errorStatus;
}

int isFileExist(const char *path)
{
    FILE *fp = fopen(path,"r");
    if(fp == NULL)
    {
        return 0;
    }
    fclose(fp);
    return 1;
}

int isDirectory(const char *path) 
{
   struct stat statbuf;
   if (stat(path, &statbuf) != 0)
    {
        return 0;
    }
   return S_ISDIR(statbuf.st_mode);
}
